home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Medal Software 2
/
Gold Medal Software Volume 2 (Gold Medal) (1994).iso
/
prog
/
asm_0_m.arj
/
GPFILT.ASM
< prev
next >
Wrap
Assembly Source File
|
1988-04-20
|
27KB
|
573 lines
page ,132
TITLE GPFILT
subttl General Purpose Filter Template
;
; GPFILT.ASM
; This file contains a template for a general-purpose assembly language
; filter program.
;
; Fill in the blanks for what you wish to do. The program is set up to
; accept a command line in the form:
; COMMAND [{-|/}options] [infile [outfile]]
;
; If infile is not specified, stdin is used.
; If outfile is not specified, stdout is used.
;
; To compile and link:
; MASM GPFILT ;
; LINK GPFILT ;
; EXE2BIN GPFILT GPFILT.COM
;
; Standard routines supplied in the general shell are:
;
; get_arg - returns the address of the next command line argument in
; DX. Since this is a .COM file, the routine assumes DS will
; be the same as the command line segment.
; The routine will return with Carry set when it reaches the end
; of the command line.
;
; err_msg - displays an ASCIIZ string on the STDERR device. Call with the
; address of the string in ES:DX.
;
; do_usage- displays the usage message on the STDERR device and exits
; with an error condition (errorlevel 1). This routine will
; never return.
;
; getch - returns the next character from the input stream in AL.
; It will return with carry set if an error occurs during read.
; It will return with the ZF set at end of file.
;
; putch - writes a character from AL to the output stream. Returns with
; carry set if a write error occurs.
;
cseg segment
assume cs:cseg, ds:cseg, es:cseg, ss:cseg
org 0100h ;for .COM files
start: jmp main ;jump around data area
;
; Equates and global data area.
;
; The following equates and data areas are required by the general filter
; routines. User data area follows.
;
STDIN equ 0
STDOUT equ 1
STDERR equ 2
STDPRN equ 3
cr equ 0dh
lf equ 0ah
space equ 32
tab equ 9
infile dw STDIN ;default input file is stdin
outfile dw STDOUT ;default output file is stdout
errfile dw STDERR ;default error file is stderr
prnfile dw STDPRN ;default print file is stdprn
cmd_ptr dw 0081h ;address of first byte of command tail
PSP_ENV equ 002ch ;The segment address of the environment
;block is stored here.
infile_err db cr, lf, 'Error opening input file', 0
outfile_err db cr, lf, 'Error opening output file', 0
aborted db 07, cr, lf, 'Program aborted', 0
usage db cr, lf, 'Usage: ', 0
crlf db cr, lf, 0
;************************************************************************
;* *
;* Buffer sizes for input and output files. The buffers need not be *
;* the same size. For example, a program that removes tabs from a text *
;* file will output more characters than it reads. Therefore, the *
;* output buffer should be slightly larger than the input buffer. In *
;* general, the larger the buffer, the faster the program will run. *
;* *
;* The only restriction here is that the combined size of the buffers *
;* plus the program code and data size cannot exceed 64K. *
;* *
;* The easiest way to determine maximum available buffer memory is to *
;* assemble the program with minimum buffer sizes and examine the value *
;* of the endcode variable at the end of the program. Subtracting this *
;* value from 65,536 will give you the total buffer memory available. *
;* *
;************************************************************************
;
INNBUF_SIZE equ 31 ;size of input buffer (in K)
OUTBUF_SIZE equ 31 ;size of output buffer (in K)
;
;************************************************************************
;* *
;* Data definitions for input and output buffers. DO NOT modify these *
;* definitions unless you know exactly what it is you're doing! *
;* *
;************************************************************************
;
; Input buffer
ibfsz equ 1024*INNBUF_SIZE ;input buffer size in bytes
inbuf equ endcode ;input buffer
ibfend equ inbuf + ibfsz ;end of input buffer
;
; ibfptr is initialized to point past end of input buffer so that the first
; call to getch will result in a read from the file.
;
ibfptr dw inbuf+ibfsz
; output buffer
obfsz equ 1024*OUTBUF_SIZE ;output buffer size in bytes
outbuf equ ibfend ;output buffer
obfend equ outbuf + obfsz ;end of output buffer
obfptr dw outbuf ;start at beginning of buffer
;************************************************************************
;* *
;* USER DATA AREA *
;* *
;* Insert any data declarations specific to your program here. *
;* *
;* NOTE: The prog_name, use_msg, and use_msg1 variables MUST be *
;* defined. *
;* *
;************************************************************************
;
; This is the program name. Under DOS 3.x, this is not used because we
; can get the program name from the environment. Prior to 3.0, this
; information is not supplied by the OS.
;
prog_name db 'GPFILT', 0
;
; This is the usage message. The first two lines are required.
; The first line is the programs title line.
; Make sure to include the 0 at the end of the first line!!
; The second line shows the syntax of the program.
; Following lines (which are optional), are discussion of options, features,
; etc...
; The message MUST be terminated by a 0.
;
use_msg db ' - General Purpose FILTer program.', cr, lf, 0
use_msg1 label byte
db '[{-|/}options] [infile [outfile]]', cr, lf
db cr, lf
db 'If infile is not specified, STDIN is used', cr, lf
db 'If outfile is not specified, STDOUT is used', cr, lf
db 0
;
;************************************************************************
;* *
;* The main routine parses the command line arguments, opens files, and *
;* does other initialization tasks before calling the filter procedure *
;* to do the actual work. *
;* For a large number of filter programs, this routine will not need to *
;* be modified. Options are parsed in the get_options proc., and the *
;* filter proc. does all of the 'filter' work. *
;* *
;************************************************************************
;
main: cld
call get_options ;process options
jc gofilter ;carry indicates end of arg list
mov ah,3dh ;open file
mov al,0 ;read access
int 21h ;open the file
mov word ptr ds:[infile], ax ;save file handle
jnc main1 ;carry clear indicates success
mov dx,offset infile_err
jmp short err_exit
main1: call get_arg ;get cmd line arg in DX
jc gofilter ;carry indicates end of arg list
mov ah,3ch ;create file
mov cx,0 ;normal file
int 21h ;open the file
mov word ptr ds:[outfile],ax ;save file handle
jnc gofilter ;carry clear indicates success
mov dx,offset outfile_err
jmp short err_exit
gofilter:
call filter ;do the work
jc err_exit ;exit immediately on error
mov ah,3eh
mov bx,word ptr [infile]
int 21h ;close input file
mov ah,3eh
mov bx,word ptr [outfile]
int 21h ;close output file
mov ax,4c00h
int 21h ;exit with no error
err_exit:
call err_msg ;output error message
mov dx,offset aborted
call err_msg
mov ax,4c01h
int 21h ;and exit with error
;
;************************************************************************
;* *
;* get_options processes any command line options. Options are *
;* preceeded by either - or /. There is a lot of flexibility here. *
;* Options can be specified separately, or as a group. For example, *
;* the command "GPFILT -x -y -z" is equivalent to "GPFILT -xyz". *
;* *
;* This routine MUST return the address of the next argument in DX or *
;* carry flag set if there are no more options. In other words, return *
;* what was returned by the last call to get_arg. *
;* *
;************************************************************************
;
get_options proc
call get_arg ;get command line arg
jnc opt1
; If at least one argument is required, use this line
; call do_usage ;displays usage msg and exits
; If there are no required args, use this line
ret ;if no args, just return
opt1: mov di, dx
mov al,byte ptr ds:[di]
cmp al,'-' ;if first character of arg is '-'
jz opt_parse
cmp al,'/' ;or '/', then get options
jz opt_parse
ret ;otherwise exit
opt_parse:
inc di
mov al,byte ptr ds:[di]
or al,al ;if end of options string
jz nxt_opt ;get cmd. line arg
cmp al,'?' ;question means show usage info
jz do_usage
;
;************************************************************************
;* *
;* Code for processing other options goes here. The current option *
;* character is in AL, and the remainder of the option string is pointed*
;* to by DS:DI. *
;* *
;************************************************************************
;
jmp short opt_parse
nxt_opt:
call get_arg ;get next command line arg
jnc opt1 ;if carry
vld_args: ;then validate arguments
;
;************************************************************************
;* *
;* Validate arguments. If some options are mutually exclusive/dependent*
;* use this area to validate them. Whatever the case, if you must *
;* abort the program, call the do_usage procedure to display the usage *
;* message and exit the program. *
;* *
;************************************************************************
;
ret ; no more options
;
;************************************************************************
;* *
;* Filter does all the work. Modify this routine to do what it is you *
;* need done. *
;* *
;************************************************************************
;
filter proc
call getch ;get a character from input into AL
jbe filt_done ;exit on error or EOF
and al, 7fh ;strip the high bit
call putch ;and output it
jc filt_ret ;exit on error
jmp short filter
filt_done:
jc filt_ret ;carry set is error
call write_buffer ;output what remains of the buffer
filt_ret:
ret
filter endp
;
;************************************************************************
;* *
;* Put any program-specific routines here *
;* *
;************************************************************************
;
;************************************************************************
;* *
;* For most programs, nothing beyond here should require modification. *
;* The routines that follow are standard routines used by almost every *
;* filter program. *
;* *
;************************************************************************
;
;************************************************************************
;* *
;* This routine outputs the usage message to the STDERR device and *
;* aborts the program with an error code. A little processing is done *
;* here to get the program name and format the output. *
;* *
;************************************************************************
;
do_usage:
mov dx, offset crlf
call err_msg ;output newline
mov ah,30h ;get DOS version number
int 21h
sub al,3 ;check for version 3.x
jc lt3 ;if carry, earlier than 3.0
;
; For DOS 3.0 and later the full pathname of the file used to load this
; program is stored at the end of the environment block. We first scan
; all of the environment strings in order to find the end of the env, then
; scan the load pathname looking for the file name.
;
push es
mov ax, word ptr ds:[PSP_ENV]
mov es, ax ;ES is environment segment address
mov di, 0
mov cx, 0ffffh ;this ought to be enuf
xor ax, ax
getvar: scasb ;get char
jz end_env ;end of environment
gv1: repnz scasb ;look for end of variable
jmp short getvar ;and loop 'till end of environment
end_env:
inc di
inc di ;bump past word count
;
; ES:DI is now pointing to the beginning of the pathname used to load the
; program. We will now scan the filename looking for the last path specifier
; and use THAT address to output the program name. The program name is
; output WITHOUT the extension.
;
mov dx, di
fnloop: mov al, byte ptr es:[di]
or al, al ;if end of name
jz do30 ;then output it
inc di
cmp al, '\' ;if path specifier
jz updp ;then update path pointer
cmp al, '.' ;if '.'
jnz fnloop
mov byte ptr es:[di-1], 0 ;then place a 0 so we don't get ext
jmp short fnloop ; when outputting prog name
updp: mov dx, di ;store
jmp short fnloop
;
; ES:DX now points to the filename of the program loaded (without extension).
; Output the program name and then go on with rest of usage message.
;
do30: call err_msg ;output program name
pop es ;restore
jmp short gopt3
;
; We arrive here if the current DOS version is earlier than 3.0. Since the
; loaded program name is not available from the OS, we'll output the name
; entered in the 'prog_name' field above.
;
lt3: mov dx, offset prog_name
call err_msg ;output the program name
;
; After outputting program name, we arrive here to output the rest of the
; usage message. This code assumes that the usage message has been
; written as specified in the data area.
;
gopt3: mov dx, offset use_msg
call err_msg ;output the message
mov dx, offset usage
call err_msg
mov dx, offset use_msg1
call err_msg
mov ax,4c01h
int 21h ;and exit with error
get_options endp
;
;************************************************************************
;* *
;* Output a message (ASCIIZ string) to the standard error device. *
;* Call with address of error message in ES:DX. *
;* *
;************************************************************************
;
err_msg proc
cld
mov di,dx ;string address in di
mov cx,0ffffh
xor ax,ax
repnz scasb ;find end of string
xor cx,0ffffh
dec cx ;CX is string length
push ds
mov ax,es
mov ds,ax ;DS is segment address
mov ah,40h
mov bx,word ptr cs:[errfile]
int 21h ;output message
pop ds
ret
err_msg endp
;
;************************************************************************
;* *
;* getch returns the next character from the file in AL. *
;* Returns carry = 1 on error *
;* ZF = 1 on EOF *
;* Upon exit, if either Carry or ZF is set, the contents of AL is *
;* undefined. *
;* *
;************************************************************************
;
; Local variables used by the getch proc.
eof db 0 ;set to 1 when EOF reached in read
last_ch dw ibfend ;pointer to last char in buffer
getch proc
mov si,word ptr ds:[ibfptr] ;get input buffer pointer
cmp si,word ptr ds:[last_ch];if not at end of buffer
jz getch_eob
getch1: lodsb ;character in AL
mov word ptr ds:[ibfptr],si ;save buffer pointer
or ah,1 ;will clear Z flag
ret ;and done
getch_eob: ;end of buffer processing
cmp byte ptr ds:[eof], 1 ;end of file?
jnz getch_read ;nope, read file into buffer
getch_eof:
xor ax, ax ;set Z to indicate EOF
ret ;and return
getch_read: ; Read the next buffer full from the file.
mov ah,3fh ;read file function
mov bx,word ptr ds:[infile] ;input file handle
mov cx,ibfsz ;#characters to read
mov dx,offset inbuf ;read into here
int 21h ;DOS'll do it for us
jc read_err ;Carry means error
or ax,ax ;If AX is zero,
jz getch_eof ;we've reached end-of-file
add ax,offset inbuf
mov word ptr ds:[last_ch],ax;and save it
mov si,offset inbuf
jmp short getch1 ;and finish processing character
read_err: ;return with error and...
mov dx,offset read_err_msg ; DX pointing to error message string
ret
read_err_msg db 'Read error', cr, lf, 0
getch endp
;
;************************************************************************
;* *
;* putch writes the character passed in AL to the output file. *
;* Returns carry set on error. The character in AL is retained. *
;* *
;************************************************************************
;
putch proc
mov di,word ptr ds:[obfptr] ;get output buffer pointer
stosb ;save the character
mov word ptr ds:[obfptr],di ;and update buffer pointer
cmp di,offset obfend ;if buffer pointer == buff end
clc
jnz putch_ret
push ax
call write_buffer ;then we've got to write the buffer
pop ax
putch_ret:
ret
putch endp
;
;************************************************************************
;* *
;* write_buffer writes the output buffer to the output file. *
;* This routine should not be called except by the putch proc. and at *
;* the end of all processing (as demonstrated in the filter proc). *
;* *
;************************************************************************
;
write_buffer proc ;write buffer to output file
mov ah, 40h ;write to file function
mov bx, word ptr ds:[outfile];output file handle
mov cx, word ptr ds:[obfptr]
sub cx, offset outbuf ;compute #bytes to write
mov dx, offset outbuf ;from this buffer
int 21h ;DOS'll do it
jc write_err ;carry is error
or ax,ax ;return value of zero
jz putch_full ;indicates disk full
mov word ptr ds:[obfptr],offset outbuf
clc
ret
putch_full: ;disk is full
mov dx,offset disk_full
stc ;exit with error
ret
write_err: ;error occured during write
mov dx,offset write_err_msg
stc ;return with error
ret
write_err_msg db 'Write error', cr, lf, 0
disk_full db 'Disk full', cr, lf, 0
write_buffer endp
;
;************************************************************************
;* *
;* get_arg - Returns the address of the next command line argument in *
;* DX. The argument is in the form of an ASCIIZ string. *
;* Returns Carry = 1 if no more command line arguments. *
;* Upon exit, if Carry is set, the contents of DX is undefined. *
;* *
;************************************************************************
;
get_arg proc
mov si,word ptr [cmd_ptr]
skip_space: ;scan over leading spaces and commas
lodsb
cmp al,0 ;if we get a null
jz sk0
cmp al,cr ;or a CR,
jnz sk1
sk0: stc ;set carry to indicate failure
ret ;and exit
sk1: cmp al,space
jz skip_space ;loop until no more spaces
cmp al,','
jz skip_space ;or commas
cmp al,tab
jz skip_space ;or tabs
mov dx,si ;start of argument
dec dx
get_arg1:
lodsb ;get next character
cmp al,cr ;argument seperators are CR,
jz get_arg2
cmp al,space ;space,
jz get_arg2
cmp al,',' ;comma,
jz get_arg2
cmp al,tab ;and tab
jnz get_arg1
get_arg2:
mov byte ptr ds:[si-1], 0 ;delimit argument with 0
cmp al, cr ;if char is CR then we've reached
jnz ga2 ; the end of the argument list
dec si
ga2: mov word ptr ds:[cmd_ptr], si ;save for next time 'round
ret ;and return
get_arg endp
endcode equ $
cseg ends
end start